package net.chathu.airgw.crypto 
{
	import air.update.utils.StringUtils;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import net.chathu.airgw.util.ByteArrayUtil;

	/**
	 * ...
	 * @author ze
	 */
	public class CipherInputStream 
	{
		
		//private static var DETAILES:Boolean = false;
		public static var UTFMAXIMAL:int = 0x40000;
		public static var MAXTRY:int = 3;
		public static var DELAY:int = 2000;
		private var incipher:Cipher;
		private var _in:Socket;
		
		public function CipherInputStream(socket:Socket, cipher:Cipher) 
		{
			_in = socket;
			incipher = 	cipher;
		}
		
		public function read():int {
			if (_in.bytesAvailable>0) { //TODO: check this
				var i:int = _in.readByte();
				var abyte0:Array = [i & 0xFF];
				incipher.decryptArray(abyte0);
				var decrypted:int = abyte0[0];
				return decrypted >= 0 ? decrypted : 255 - ~decrypted;
			} else {
				return -1;
			}
		}
		
		
		/*public function read(abyte0:Array):int
		{
			return read(abyte0, 0, abyte0.length);
		}*/
		
		public function readBytes(abyte0:Array, i:int, j:int):int
		{
			//var k:int = _in.read(abyte0, i, j);
			//flashben mindet beolvassa? igy k mindig = j
			var k:int = j;
			var bytearray:ByteArray = new ByteArray();
			_in.readBytes(bytearray, 0, j);
			bytearray.position = 0;
			for (var c:int = 0; c < j; c++)
			{
				abyte0[i + c] = bytearray.readByte();
			}
			
			trace("Decrypted read " + j + " bytes");
			if(k > 0)
				incipher.decrypt(abyte0, i, abyte0, i, k);
			return k;
		}
		/* 
		public int read(byte abyte0[], int i, int j)
			throws IOException
		{
			int k = in.read(abyte0, i, j);
			if(k > 0)
				incipher.decrypt(abyte0, i, abyte0, i, k);
			return k;
		}
		*/
		public function readBytes_BA(bytearray:ByteArray, offset:int, length:int):int {
			var k:int = length;
			_in.readBytes(bytearray, offset, length);
			var array:Array = ByteArrayUtil.byteArrayToArray(bytearray);
			
			incipher.decrypt(array, offset, array, offset, k);
			bytearray.position = 0;
			for (var c:int = 0; c < array.length; c++)
			{
				bytearray.writeByte(array[c]);
			}
			return k;
			
		}

		public final function readFully(abyte0:Array, i:int, j:int):void
		{
			if (j < 0)
			{
				//throw new IndexOutOfBoundsException();
				throw new Error("IndexOutOfBoundsException");
			}
			var k:int = 0;
			var l:int = 0;
			while(k < j) 
			{
				var i1:int = readBytes(abyte0, i + k, j - k);
				if(i1 < 0)
				{
					/*if(l < MAXTRY)
					{
						try
						{
							wait(DELAY);
						}
						catch(InterruptedException _ex) { }
						l++;
					} else
					{
						throw new EOFException();
					}*/
					throw new Error("EOFException");
				} else
				{
					l = 0;
					k += i1;
				}
			}
		}
		
		 public function readInt():int
		{
			var abyte0:Array = [0,0,0,0]
			readFully(abyte0, 0, 4);
			//trace("UTF LEN dump:" );
			//ByteArrayDump.dumpArray(abyte0);
			return (abyte0[0] >= 0 ? abyte0[0] : 255 - ~abyte0[0]) << 24 | (abyte0[1] >= 0 ? abyte0[1] : 255 - ~abyte0[1]) << 16 | (abyte0[2] >= 0 ? abyte0[2] : 255 - ~abyte0[2]) << 8 | (abyte0[3] >= 0 ? abyte0[3] : 255 - ~abyte0[3]);
		}

		public function readUTFWithLimit(i:int):String
		{
			if (i > UTFMAXIMAL)
			{
				throw new Error("UTFDataFormatException, read limit:" + i);
				//throw new UTFDataFormatException();
			}
			if (i < 0)
			{
				throw new Error("UTFDataFormatException");
				//throw new UTFDataFormatException();
			}
			//StringBuffer stringbuffer = new StringBuffer(i);
			var stringbuffer:String = new String();
			//byte abyte0[] = new byte[i];
			var abyte0:Array = new Array();
			for (var c:int = 0; c < i; c++) { abyte0.push(0);}
			
			var k:int = 0;
			readFully(abyte0, 0, i);
			while(k < i) 
			{
				var j:int = abyte0[k] & 0xff;
				switch(j >> 4)
				{
					case 0: // '\0'
					case 1: // '\001'
					case 2: // '\002'
					case 3: // '\003'
					case 4: // '\004'
					case 5: // '\005'
					case 6: // '\006'
					case 7: // '\007'
						k++;
						//stringbuffer.append((char)j);
						stringbuffer += String.fromCharCode(j);
					break;

					case 12: // '\f'
					case 13: // '\r'
						if ((k += 2) > i)
						{
							//throw new UTFDataFormatException();
							throw new Error("UTFDataFormatException");
						}
						//byte byte0 = abyte0[k - 1];
						var byte0:int = abyte0[k - 1];
						if ((byte0 & 0xc0) != 128)
						{
							//throw new UTFDataFormatException();
							throw new Error("UTFDataFormatException");
						}
						//stringbuffer.append((char)((j & 0x1f) << 6 | byte0 & 0x3f));
						stringbuffer += String.fromCharCode(((j & 0x1f) << 6 | byte0 & 0x3f));
					break;

					case 14: // '\016'
						if ((k += 3) > i)
						{
							throw new Error("UTFDataFormatException");
							//throw new UTFDataFormatException();
						}
						//byte byte1 = abyte0[k - 2];
						var byte1:int = abyte0[k - 2];
						//byte byte2 = abyte0[k - 1];
						var byte2:int = abyte0[k - 1];
						if ((byte1 & 0xc0) != 128 || (byte2 & 0xc0) != 128)
						{
							//throw new UTFDataFormatException();
							throw new Error("UTFDataFormatException");
						}
						//stringbuffer.append((char)((j & 0xf) << 12 | (byte1 & 0x3f) << 6 | byte2 & 0x3f));
						stringbuffer += String.fromCharCode(((j & 0xf) << 12 | (byte1 & 0x3f) << 6 | byte2 & 0x3f));
					break;

					case 8: // '\b'
					case 9: // '\t'
					case 10: // '\n'
					case 11: // '\013'
					default:
						throw new Error("UTFDataFormatException");
						//throw new UTFDataFormatException();
					break;
				}
			}
			//return new String(stringbuffer);
			return stringbuffer;
		}
		
		
		public function readUTF():String
		{
			var i:int = readInt();
			return readUTFWithLimit(i);
		}
		
		public function get bytesAvailable():int {
			return _in.bytesAvailable;
		}
	}
}